
#ifndef RTHTTPHEADPARSER_H
#define RTHTTPHEADPARSER_H

#include "RtHttpChunkedDecoder.h"
#include "RtMessageBlock.h"

class CRtChannelHttpClient;

template <class HeadType, class HandleType>
class CRtHttpParserT  
{
public:
	CRtHttpParserT(HeadType &aHeader, HandleType *aType = NULL)
		: m_Header(aHeader)
		, m_pChunkedDecoder(NULL)
		, m_bSkipContentLength(FALSE)
		, m_bDeliverEach(FALSE)
		, m_HandleOwner(aType)
	{
		Clear();
	}

	~CRtHttpParserT()
	{
		m_pChunkedDecoder = NULL;
	}

	void Clear()
	{
		m_Header.Clear();
		m_strLineBuf.resize(0);
		m_strAllContent.resize(0);
		m_bStartFired = FALSE;
		m_dwContentLength = 0;
		m_dwContentRead = 0;
		m_pChunkedDecoder = NULL;
		m_bFinished = FALSE;
	}

	// return value:
	// RT_ERROR_PARTIAL_DATA: not enough data.
	// RT_OK: successful.
	// others: failed.
	// aData will AdavanceReadPtr automatically.
	RtResult Analyse(CRtMessageBlock &aData)
	{
		RtResult rv = RT_OK;
		//RT_ASSERTE_RETURN(!m_bFinished, rv);
		
		if (!m_Header.HaveAllHeaders()) {
			rv = ParseHead(aData);
			if (RT_FAILED(rv))
				return rv;
		}

		if (!m_Header.HaveAllHeaders())
			return RT_ERROR_PARTIAL_DATA;

		return HandleContent(aData);
	}

	RtResult GetContent(CRtMessageBlock *&aMb)
	{
		if(!m_bDeliverEach)
			RT_ASSERTE_RETURN(m_bFinished, RT_ERROR_NOT_INITIALIZED);
		
		aMb = NULL;
		if (m_strAllContent.length() <= 0)
			return RT_OK;
		
		CRtMessageBlock mb(
			m_strAllContent.length(), 
			const_cast<char*>(m_strAllContent.c_str()),
			CRtMessageBlock::DONT_DELETE,
			m_strAllContent.length());
		aMb = mb.DuplicateChained();
		if(m_bDeliverEach)
		{
			m_strAllContent.erase(0, m_strAllContent.length());
		}
		return aMb ? RT_OK : RT_ERROR_OUT_OF_MEMORY;
	}

	BOOL IsFinished() { return m_bFinished; }
	void SetDeleverFlag(BOOL bFlag = FALSE)
	{
		m_bDeliverEach = bFlag;
	}

	void OnParseFinished()
	{
	//	RT_INFO_TRACE_THIS("CRtHttpParserT::OnParseFinished");
		if(m_HandleOwner) m_HandleOwner->OnResponseFinished();
	}
private:
	RtResult ParseHead(CRtMessageBlock &aData)
	{
		RT_ASSERTE(!m_Header.HaveAllHeaders());

		CRtString strHead = aData.FlattenChained();
		LPCSTR pszBuf = strHead.c_str();
		DWORD dwBufLen = strHead.length();
		DWORD dwCountRead = 0;

		DWORD nLineLen = 0;
		LPCSTR szLine = NULL;
		while ((szLine = (const char*)(
			memchr(pszBuf, '\n', dwBufLen - dwCountRead))) != NULL)
		{
			if (szLine == pszBuf || *(szLine-1) != '\r') {
				RT_ERROR_TRACE_THIS("CRtHttpParserT::ParseHead, not \"\\r\\n\"!");
				return RT_ERROR_FAILURE;
			}
			nLineLen = szLine - pszBuf + 1;
			dwCountRead += nLineLen;
			m_strLineBuf.append(pszBuf, nLineLen);

			if (!m_Header.ParseLine(m_strLineBuf))
				return RT_ERROR_FAILURE;

			m_strLineBuf.resize(0);
			if (m_Header.HaveAllHeaders()) {
				aData.AdvanceChainedReadPtr(dwCountRead);
				return RT_OK;
			}
			pszBuf = szLine + 1;
		}
		if (!m_Header.HaveAllHeaders() && (nLineLen = dwBufLen - dwCountRead) != 0)
			m_strLineBuf.append(pszBuf, nLineLen);
		aData.AdvanceChainedReadPtr(dwCountRead);
		return RT_OK;
	}

    RtResult HandleContent(CRtMessageBlock &aData)
	{
		if (!m_bStartFired) {
			if (m_bSkipContentLength) {
				m_dwContentLength = 0;
			}
			else {
				m_dwContentLength = m_Header.GetContentLength();
				CRtString strVal;
				m_Header.GetHeader(CRtHttpAtomList::Transfer_Encoding, strVal);
				if (!strcasecmp(strVal.c_str(), "chunked")) {
					RT_ASSERTE(!m_pChunkedDecoder);
					m_pChunkedDecoder = new CRtHttpChunkedDecoder<CRtHttpParserT<HeadType, HandleType> >(this);
					if (!m_pChunkedDecoder)
						return RT_ERROR_OUT_OF_MEMORY;
					m_dwContentLength = (DWORD)-1;
				} else if (m_dwContentLength == (DWORD)-1) {
// 					RT_WARNING_TRACE_THIS("CRtHttpParserT::HandleContent, ContentLength is -1 and no 'chunked'!");
					m_dwContentLength = 0;
				}
			}
			m_bStartFired = TRUE;
		}

		CRtMessageBlock *pMbMove = &aData;
		while (pMbMove) {
			if (m_pChunkedDecoder) {
				DWORD dwHaveRead;
				if (!m_pChunkedDecoder->HandleChunkedContent(
					pMbMove->GetTopLevelReadPtr(), 
					pMbMove->GetTopLevelLength(), 
					m_strAllContent,
					dwHaveRead))
				{
					return RT_ERROR_FAILURE;
				}
				pMbMove->AdvanceTopLevelReadPtr(dwHaveRead);
			}
			else {
#if 0//jobs 2007.04.10
				m_dwContentRead += pMbMove->GetTopLevelLength();
				if (m_dwContentRead > m_dwContentLength) {
					RT_WARNING_TRACE_THIS("CRtHttpParserT::HandleContent,"
						" m_dwContentRead=" << m_dwContentRead << 
						" > m_dwContentLength=" << m_dwContentLength);
					DWORD dwLen = pMbMove->GetTopLevelLength() + m_dwContentLength - m_dwContentRead;
					m_strAllContent.append(
						pMbMove->GetTopLevelReadPtr(), 
						dwLen);
					pMbMove->AdvanceTopLevelReadPtr(dwLen);
					m_dwContentRead = m_dwContentLength;
					break;
				}
				else {
					m_strAllContent.append(pMbMove->GetTopLevelReadPtr(), pMbMove->GetTopLevelLength());
					pMbMove->AdvanceTopLevelReadPtr(pMbMove->GetTopLevelLength());
				}
#else
				m_dwContentRead += pMbMove->GetTopLevelLength();
				if (m_dwContentRead > m_dwContentLength) {
					RT_WARNING_TRACE_THIS("CRtHttpParserT::HandleContent,"
						" m_dwContentRead=" << m_dwContentRead << 
						" > m_dwContentLength=" << m_dwContentLength);
					DWORD dwLen = pMbMove->GetTopLevelLength() + m_dwContentLength - m_dwContentRead;
					if(m_dwContentLength == 0)
						dwLen = m_dwContentLength = pMbMove->GetTopLevelLength();
					
					m_strAllContent.append(
						pMbMove->GetTopLevelReadPtr(), 
						dwLen);
					pMbMove->AdvanceTopLevelReadPtr(dwLen);
					m_dwContentRead = m_dwContentLength;
					break;
				}
				else {
					m_strAllContent.append(pMbMove->GetTopLevelReadPtr(), pMbMove->GetTopLevelLength());
					pMbMove->AdvanceTopLevelReadPtr(pMbMove->GetTopLevelLength());
				}

#endif

			}
			pMbMove = pMbMove->GetNext();
		}
		if (m_dwContentRead == m_dwContentLength || 
			(m_pChunkedDecoder && m_pChunkedDecoder->ReachedEOF())) 
		{
			m_bFinished = TRUE;
			if(m_HandleOwner) m_HandleOwner->OnResponseFinished();
			return RT_OK;
		}
		else if(m_bDeliverEach) //deliver all partial data
			return RT_OK;
		else
			return RT_ERROR_PARTIAL_DATA;
	}


private:
	HeadType &m_Header;
	CRtString m_strLineBuf;
	CRtAutoPtr<CRtHttpChunkedDecoder<CRtHttpParserT<HeadType, HandleType> > > m_pChunkedDecoder;
	BOOL m_bStartFired;
	BOOL m_bFinished;
	CRtString m_strAllContent;
	DWORD m_dwContentLength;
	DWORD m_dwContentRead;

public:
	BOOL m_bSkipContentLength;
	BOOL m_bDeliverEach;

	HandleType *m_HandleOwner;
};

#endif // !RTHTTPHEADPARSER_H
